home *** CD-ROM | disk | FTP | other *** search
/ Internet Publisher's Toolbox 2.0 / Internet Publisher's Toolbox.iso / internet / ntserver / wtsource / irtfiles.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-30  |  48.9 KB  |  1,614 lines

  1. /* WIDE AREA INFORMATION SERVER SOFTWARE:
  2.    No guarantees or restrictions.  See the readme file for the full standard
  3.    disclaimer.
  4.  
  5.    Brewster@think.com
  6. */
  7.  
  8. /* Copyright (c) CNIDR (see ../COPYRIGHT) */
  9.  
  10.  
  11. /* Change log:
  12.  * $Log:    irtfiles.c,v $
  13.  *
  14.  * Revision 1.4a  93/07/23  02:19:27  warnock
  15.  * corrected arguments in call to map_over_words in finish_document
  16.  *
  17.  * Revision 1.4  1993/09/22  16:07:52  pfeifer
  18.  * Fixed word breaking for german ISO Umlaute and sz
  19.  * 
  20.  * Revision 1.3a  93/07/19  17:06:03  warnock
  21.  * fixed problem with multiple documents in single file, from isaacs@hpcc05.corp.hp.com
  22.  *
  23.  * Revision 1.3  1993/06/04  10:23:15  pfeifer
  24.  * Pachtlevel BIBDB
  25.  * 
  26.  * Revision 1.2a  93/07/19  16:31:27  warnock
  27.  * Added document type URL from Nathan.Torkington@vuw.ac.nz
  28.  *
  29.  * Revision 1.2  1993/06/01  14:05:54  pfeifer
  30.  * Added code for soundex/phonix indexing and retrieval
  31.  * 
  32.  * Revision 1.1  1993/02/16  15:05:35  freewais
  33.  * Initial revision
  34.  *
  35.  * Revision 1.32  92/05/06  17:32:14  jonathan
  36.  * Added new global for current_filename and current_filecount (from
  37.  * riddle@rice.edu).
  38.  * 
  39.  * Revision 1.31  92/04/30  12:25:09  jonathan
  40.  * changed a couple of s_free's to free's for ULTRIX CC.
  41.  * 
  42.  * Revision 1.30  92/04/29  08:09:55  shen
  43.  * add global variable "_indexable_section", default is true
  44.  * 
  45.  * Revision 1.29  92/04/28  17:53:24  jonathan
  46.  * Replaced directory routines with scandir.
  47.  * 
  48.  * Revision 1.28  92/03/20  11:02:55  jonathan
  49.  * Added code to handle switches for word_pairs and word_postition info.
  50.  * 
  51.  * Revision 1.27  92/02/13  11:23:21  jonathan
  52.  * Removed printable_time() from index logging, since it's done by waislog.
  53.  * 
  54.  * Revision 1.26  92/02/12  13:31:29  jonathan
  55.  * Added "$Log" so RCS will put the log message in the header
  56.  * 
  57. */
  58.  
  59. /* 
  60.  * Indexes the words in a text file.
  61.  * 
  62.  * Port of irtfiles.lisp.
  63.  *
  64.  * -brewster 6/90
  65.  */
  66.  
  67. /* the main functions are:
  68.  *   index_text_file
  69.  *   index_directory
  70.  *
  71.  * Some of the policy issues coded in this file are
  72.  *   What extra weight should the headline get?
  73.  *
  74.  */
  75.  
  76. #include <ctype.h>
  77. #include <string.h>
  78. #include "panic.h"  
  79. #include "irdirent.h"
  80. #include "irhash.h"
  81. #include "cutil.h"
  82. #include "futil.h"
  83. #include "irfiles.h"
  84. #include "irtfiles.h"
  85.  
  86. #include "ircfiles.h"   /* dgg, need for genbank_header_function test */
  87. #include "stemmer.h"   
  88.  
  89. #ifdef SOUND
  90. #include "soundex.h"
  91. #endif
  92.  
  93. #ifndef THINK_C
  94. #include <sys/types.h>
  95. #include <sys/stat.h>
  96. #endif /* ndef THINK_C */
  97.  
  98. #ifdef WIN32
  99. #include <windows.h>
  100. long add_word(char*,long,long,long,long,time_t,long,database*,boolean);
  101. boolean wordbreak_isiso(long);
  102. #endif
  103.  
  104. #define MAX_LINE_LENGTH 1000 /* characters */
  105. #define extra_weight_for_header 10
  106.  
  107. #ifdef UNIX
  108. #define PRINT_AS_INDEXING true /* also defined in irfiles.c */
  109. #else 
  110. #define PRINT_AS_INDEXING false
  111. #endif
  112.  
  113. char* header_flag_1;
  114. char* header_flag_2;
  115. long len_of_files_since_last_delete = 0;
  116. long len_of_files_since_last_flush = 0;
  117. long total_indexed_file_length = 0;
  118.  
  119. boolean indexingForBeta = false;
  120.  
  121. long _indexable_section = 1;
  122.  
  123. char *current_filename = NULL;
  124. int  current_filecount = 0;
  125.  
  126. boolean index_contents = true;
  127.  
  128.  
  129. #define keyword_weight 1
  130.  
  131. /* keywords from command line (set in waisindex.c), used in finish_document */
  132.  
  133. char* keywords = NULL;
  134.  
  135. /* name of keyword file from command line, used in finish_document */
  136. char* keyword_filename = NULL;
  137.  
  138. #ifdef WIN32
  139. /* excluded filename(s) from command line, used in index_text_file */
  140. /* File names are separated by \0 and terminated by \0\0 */
  141. char ExcludeFiles[EXCLUDEFILENAMESLEN] = "";
  142. #endif
  143.  
  144.  
  145. #ifdef WIN32
  146.  
  147. /*
  148.  *  Return non-zero if the supplied trial matches the Mask string,
  149.  *  where a ? in the Mask matched anything in the Trial.
  150.  */
  151. static int MatchFileNameComponent(char *Mask,char *Trial) {
  152. int i;
  153.  
  154.     i = 0;
  155.     while (Trial[i] && Mask[i]) {
  156.         if (Mask[i]!='?') {
  157.             if (Trial[i]!=Mask[i]) return 0;
  158.         }
  159.         i++;
  160.     }
  161.     if (Trial[i]) {
  162.         /* Ran out of Mask before end of Trial */
  163.         return 0;
  164.     } else {
  165.         /* Ran out of Trial - is remainder of mask just ??? */
  166.         while (Mask[i]) {
  167.             if (Mask[i]!='?') return 0;
  168.             i++;
  169.         }
  170.         return 1;
  171.     }
  172. }
  173.  
  174. /*
  175.  *  Return non-zero if the supplied file name matches the possibly
  176.  *  wildcarded file mask.
  177.  */
  178. static int WildcardMatch(char *FileMask,char *FileName) {
  179. char ExpandedMaskName[MAX_FILENAME_LEN];
  180. char ExpandedMaskExtn[MAX_FILENAME_LEN];
  181. char TempFileName[MAX_FILENAME_LEN];
  182. char *p;
  183. int i;
  184. int j;
  185.  
  186.     i = 0;
  187.     /* Expand the Name portion of the mask */
  188.     j = 0;
  189.     while (FileMask[i]) {
  190.         if (FileMask[i]=='.') {
  191.             /* We've reached the extension portion. */
  192.             break;
  193.         }
  194.         if (FileMask[i]=='*') {
  195.             /* Expand * to multiple ??? to the end of the string then break. */
  196.             while (j<MAX_FILENAME_LEN-1) ExpandedMaskName[j++] = '?';
  197.             break;
  198.         }
  199.         /* Just copy in the other characters */
  200.         ExpandedMaskName[j++] = FileMask[i++];
  201.     }
  202.     ExpandedMaskName[j] = '\0';
  203.     /* Find the extension */
  204.     while (FileMask[i]) {
  205.         if (FileMask[i]=='.') {
  206.             i++;
  207.             break;
  208.         }
  209.         i++;
  210.     }
  211.     /* Expand the Extension portion of the mask */
  212.     j = 0;
  213.     while (FileMask[i]) {
  214.         if (FileMask[i]=='*') {
  215.             /* Expand * to multiple ??? to the end of the string then break. */
  216.             while (j<MAX_FILENAME_LEN) ExpandedMaskExtn[j++] = '?';
  217.             ExpandedMaskExtn[MAX_FILENAME_LEN-1] = '\0';
  218.             break;
  219.         }
  220.         /* Just copy in the other characters */
  221.         ExpandedMaskExtn[j++] = FileMask[i++];
  222.     }
  223.     ExpandedMaskExtn[j] = '\0';
  224.     /* Change masks to upper case */
  225.     strupr(ExpandedMaskName);
  226.     strupr(ExpandedMaskExtn);
  227.     /* Duplicate the filename string cos we'll change it */
  228.     strncpy(TempFileName,FileName,MAX_FILENAME_LEN-2);
  229.     strupr(TempFileName);
  230.     TempFileName[strlen(TempFileName)+2] = '\0';    /* Ensure its doubly-null terminated */
  231.     /* Now match the file name portion against the supplied name */
  232.     p = strtok(TempFileName,".");
  233.     if (MatchFileNameComponent(ExpandedMaskName,p)) {
  234.         p += strlen(p);
  235.         p++;
  236.         if (MatchFileNameComponent(ExpandedMaskExtn,p)) {
  237.             return 1;
  238.         }
  239.     }
  240.     return 0;
  241. }
  242. #endif
  243.  
  244. /*  Handling Word Pairs */
  245.  
  246. /* makes a word_pair out of a two words:
  247. make_joint_word("abcdefghijklmnopqrstuvwxyz", "123456789012345678901");
  248.   "abcdefghij1234567890"
  249. make_joint_word("abcdefghijkl", "123");
  250.   "abcdefghij123"
  251. make_joint_word("abc", "123");
  252.   "abc123" */
  253.  
  254. char *make_joint_word(word1, word2)
  255.      char* word1;
  256.      char* word2;
  257. {
  258.   static char new_word[MAX_WORD_LENGTH + 1];
  259.   strncpy(new_word, word1, MAX_WORD_LENGTH / 2);
  260.   strncpy(new_word + MIN(MAX_WORD_LENGTH / 2, strlen(word1)),
  261.       word2, MAX_WORD_LENGTH - (MAX_WORD_LENGTH / 2));
  262.   return(new_word); 
  263. }
  264.  
  265. /* returns 0 is successful, non-0 if error */
  266. static long add_word_before_pairs _AP((char *word, long char_pos,
  267.                        long line_pos, long weight,
  268.                        long doc_id, time_t date,
  269.                        boolean capitalized, database* db,
  270.                        boolean word_position, boolean word_pairs));
  271.  
  272. static long
  273.   add_word_before_pairs(word, char_pos, line_pos,
  274.             weight, doc_id, date, capitalized, db,
  275.             word_position, word_pairs)
  276. char *word; /* the word to be indexed, this could be a
  277.            word pair. If NULL there are no more words
  278.            to be indexed */
  279. long char_pos;  /* the position of the start of the
  280.            word */
  281. long line_pos;  /* this is passed for the best
  282.            section calculation */
  283. long weight;    /* how important the word looks
  284.            syntactically (such as is it bold)
  285.            NOT used by signature system */
  286. long doc_id;    /* current document, this will never be 0 */
  287. time_t date; /* display day of this document, 0 if not known */
  288. boolean capitalized; /* if the word started with a cap */
  289. database* db; /* database to insert the document */
  290. boolean word_position; /* if true, include word position in index. */
  291. boolean word_pairs; /* if true, add pairs of capitalized words */
  292. {
  293.   static char last_word[MAX_WORD_LENGTH + 1];
  294.   static long last_doc_id = -1;
  295.   /* The way it works is it remembers if the last word if it was
  296.      capitalized (if not it clears the saved word).  
  297.      If another capitalized word comes along next
  298.      (and it is in the same document), then it makes a joint word and calls 
  299.      add_word with it. 
  300.      
  301.      This does not throw away stopwords before forming pairs, so it will 
  302.      not be quite what CMDRS does.  This should only be used in seeker 
  303.      and serial searching before proximity is used.
  304.      
  305.      */
  306.   if(capitalized && word_pairs){
  307.     if(last_word[0] != '\0' && last_doc_id == doc_id){
  308. #ifdef WIN32
  309.       add_word(make_joint_word(last_word, word), 
  310.            char_pos, line_pos, weight, doc_id, date, 1L, db, word_position);
  311. #else
  312.       add_word(make_joint_word(last_word, word), 
  313.            char_pos, line_pos, weight, doc_id, date, 1L, db);
  314. #endif
  315.     }
  316.     else{
  317.       last_word[0] = '\0';
  318.     }
  319.     strncpy(last_word, word, MAX_WORD_LENGTH);
  320.     last_doc_id = doc_id;
  321.   }
  322.   else{             /* not capitalized or word_pairs is false */
  323.     last_word[0] = '\0';
  324.   }
  325.   return(add_word(word, char_pos, line_pos, weight, doc_id, date, 0L, db, word_position));
  326. }
  327.  
  328.  
  329. #ifdef NOTUSED
  330. #define WORD_LETTERS  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
  331.  
  332.  
  333. static char *new_word _AP((char* line,char* word));
  334.  
  335. static char *new_word(line,word)
  336. char *line;
  337. char *word;
  338. {
  339.   /* This copies the first word from line into word while downcasing it.
  340.      It returns a pointer into line that is after the word,
  341.      which can be used to call this function again.
  342.      If there are no words left, then NULL is returned,
  343.      and word is length 0.
  344.      There has got to be a better way.
  345.      */
  346.   long i = 0;
  347.   char *beginning_ptr = strpbrk(line, WORD_LETTERS);
  348.   char *next_word;
  349.   long length;
  350.   if(NULL == beginning_ptr){
  351.     word[0] = '\0';
  352.     return(NULL);
  353.   }
  354.   length  = strspn(beginning_ptr, WORD_LETTERS);
  355.   next_word = length + beginning_ptr;
  356.  
  357.   length = MIN(MAX_WORD_LENGTH,length);
  358.   for(i=0; i<length; i++){
  359.     word[i] = char_downcase((unsigned long)*beginning_ptr++);
  360.   }
  361.   word[i] = '\0';
  362.   return(next_word);
  363. }
  364.  
  365. static boolean reasonable_word _AP((char* word));
  366.  
  367. static boolean reasonable_word(word)
  368. char* word;
  369. /* this should be more sophisticated */
  370. {
  371.   if(strlen(word) > 1){
  372.     return(TRUE);
  373.   }
  374.   else{
  375.     return(FALSE);
  376.   }
  377. }
  378.  
  379. #endif /* def NOTUSED */
  380.  
  381.  
  382.  
  383. /* MAPPING A FUNCTION OVER WORDS (QUICKLY) */
  384.  
  385.  
  386. /* map_over_words("foo bar baz", 0L, 1L, 0L, &integer, false, db, dummy_wordfunction) */
  387. static long dummy_wordfunction(word, char_pos, line_pos,
  388.                    weight, doc_id, date, capitalized, db)
  389.      char *word;    /* the word to be indexed, this could be a
  390.                word pair. If NULL there are no more words
  391.                to be indexed */
  392.      long char_pos; /* the position of the start of the
  393.                word */
  394.      long line_pos; /* this is passed for the best
  395.                section calculation */
  396.      long weight;   /* how important the word looks
  397.                syntactically (such as is it bold)
  398.                NOT used by signature system */
  399.      long doc_id;   /* current document, this will never be 0 */
  400.      time_t date; /* display day of this document, 0 if not known */
  401.      boolean capitalized; /* if the word started with a cap */
  402.      database* db; /* database to insert the document */
  403. {
  404.   if(word != NULL)
  405.     printf("word: %s, char_pos: %ld\n", word, char_pos);
  406.   return(0);
  407. }
  408.  
  409.  
  410.  
  411.   
  412. /* returns the number of words added, or -1 if an error occurred */
  413. long map_over_words(line,
  414.             document_id,
  415.             weight,
  416.             file_position_before_line,
  417.             line_length,
  418.             newline_terminated,
  419.             db,
  420.             wordfunction,
  421.             word_position, word_pairs,
  422. #ifdef SOUND
  423.                     minwordlen, type)   
  424. #else
  425.                     minwordlen) /* dgg */
  426. #endif
  427.  
  428. char* line;
  429. long document_id;
  430. long weight;
  431. long file_position_before_line;
  432. long *line_length;
  433. boolean *newline_terminated;
  434. database* db;
  435. wordfunc *wordfunction;
  436. boolean word_position, word_pairs;
  437. int minwordlen;
  438. #ifdef SOUND
  439. char* type;
  440. #endif
  441.  
  442. {
  443.   /* Add words to the index if it should be done. 
  444.    * Returns the number of words added.
  445.    * Should it return the amount of weight added?
  446.    * The line length is side effected with the length of the line.
  447.    * Newline_terminated is set based on whether the last character
  448.    * in the string was a newline.  If it was not, then it fgets probably
  449.    * did not retrieve the whole line.
  450.    */
  451.  
  452.   long position_in_word = 0;
  453.   long word_count = 0;
  454.   unsigned long ch;
  455.   long char_count = 0;
  456.   boolean capitalized = false; /* if the word starts with a cap */
  457.   char word[MAX_WORD_LENGTH + 1];
  458.  
  459.  
  460.   for(ch = (unsigned char)line[char_count++]; 
  461.       ch != '\0'; ch = (unsigned char)line[char_count++]){
  462. #ifdef BIO
  463.     boolean alnum = (wordDelimiter(ch) == NOT_DELIMITER);
  464. #else
  465.     boolean alnum = isalnum(ch) || wordbreak_isiso(ch);
  466. #endif
  467.  
  468.     if(alnum){
  469.       /* put the character in the word if not too long */
  470.       if(position_in_word == 0)
  471.     capitalized = isupper((unsigned long)ch)?true:false;
  472.       if(position_in_word < MAX_WORD_LENGTH){
  473.     word[position_in_word++] = char_downcase((unsigned long)ch);
  474.       }
  475.     }
  476.     else{ /* not an in a word */
  477.       if(position_in_word != 0){
  478.     /* then we have collected a word */
  479.     if(position_in_word >= minwordlen){ /* is it reasonable ? */
  480.       word[position_in_word] = '\0';
  481.  
  482. /* call the stemmer */
  483. stemmer(word);  
  484.  
  485.       if(0 !=
  486.          (*wordfunction)(word,
  487.                  file_position_before_line + char_count,  
  488.                  /*^^ dgg, this param is supposed to be start-of-word, but char_count is now at end-of-word !*/
  489.                  0L, /* line_pos */
  490.                  weight, 
  491.                  document_id, 
  492.                  (time_t)0L,
  493.                  capitalized,
  494.                  db,
  495.                  word_position,
  496.                  word_pairs))  
  497.         return(-1); /* error */
  498.       word_count++;
  499.     }
  500.  
  501. #ifdef SOUND
  502.           /*=========================== SOUNDEX / PHONIX ========================================*/
  503.         if ((word_count == 1) && (type != NULL)) /* use only the first word (i.e. the surname) for SOUNDEX/PHONIX! */
  504.           if ((!strcmp(type, "SOUNDEX")) || (!strcmp(type, "PHONIX"))) 
  505.             {
  506.               char code[20];
  507. #ifndef WIN32
  508.               int  i;
  509. #endif
  510.              
  511.               if (!strcmp(type, "SOUNDEX"))
  512.                 Soundex(word, code);
  513.               else if (!strcmp(type, "PHONIX"))
  514.                 Phonix(word, code);
  515.             
  516.               code[0] = tolower(code[0]);
  517. #ifdef DEBUG
  518.               fprintf(stderr, "%5d, %4s, %s\n", word_count, code, word); 
  519. #endif
  520.               if (0 != (*wordfunction) (code,
  521.                                         file_position_before_line + char_count, 
  522.                                         0L, /* line_pos */
  523.                                         weight, 
  524.                                         document_id, 
  525.                                         (time_t) 0L,
  526.                                         capitalized,
  527.                                         db,
  528.                                         word_position,
  529.                                         word_pairs))
  530.                 return(-1);       /* error */
  531.               
  532.               word_count++;
  533.             }
  534.           /*=====================================================================================*/
  535. #endif
  536.  
  537.         position_in_word = 0;
  538.       }
  539.     }
  540.   }
  541.   /* finish last word */
  542.   if(position_in_word >= minwordlen){ /* is it reasonable ? */
  543.     word[position_in_word] = '\0';
  544.  
  545. /* call the stemmer */
  546. stemmer(word);  
  547.  
  548.     if(0 != (*wordfunction)(word,
  549.                 file_position_before_line + char_count, 
  550.                 0L, /* line_pos */
  551.                 weight, 
  552.                 document_id, 
  553.                 (time_t)0L,
  554.                 capitalized,
  555.                 db,
  556.                 word_position, word_pairs))  
  557.       return(-1);
  558.     word_count++;
  559.   }
  560.   
  561.   /* for debugging
  562.   if(char_count - 1 != strlen(line)) {
  563.     waislog(WLOG_HIGH, WLOG_ERROR, 
  564.         "char_count: %ld, strlen: %ld", char_count, strlen(line));
  565.   }
  566.   */
  567.   if(newline_terminated != NULL){
  568.     if('\n' != line[char_count-2])
  569.       *newline_terminated = false;
  570.     else
  571.       *newline_terminated = true;
  572.   }
  573.   if(line_length != NULL)
  574.     *line_length = char_count - 1;
  575.   return(word_count);
  576. }
  577.  
  578.  
  579. static long add_words_if_appropriate 
  580.   _AP((char* line,long document_id,long weight,long file_position_before_line,
  581.        long* line_length,boolean* newline_terminated,database* db,
  582.        boolean word_position, boolean word_pairs,
  583.        int minwordlen));
  584.  
  585. static long 
  586. add_words_if_appropriate(line,
  587.              document_id,
  588.              weight,
  589.              file_position_before_line,
  590.              line_length,
  591.              newline_terminated,
  592.              db, 
  593.              word_position, word_pairs,
  594.              minwordlen)    /* dgg */
  595. char* line;
  596. long document_id;
  597. long weight;
  598. long file_position_before_line;
  599. long *line_length;
  600. boolean *newline_terminated;
  601. database* db;
  602. boolean word_position, word_pairs;
  603. int  minwordlen;
  604. {
  605.   /* Add words to the index if it should be done. 
  606.    * Returns the number of words added.
  607.    * Should it return the amount of weight added?
  608.    * The line length is side effected with the length of the line.
  609.    * Newline_terminated is set based on whether the last character
  610.    * in the string was a newline.  If it was not, then it fgets probably
  611.    * did not retrieve the whole line.
  612.    */
  613.  
  614.   long position_in_word = 0;
  615.   long word_count = 0;
  616.   char word[MAX_WORD_LENGTH + 1];
  617.   unsigned long ch;
  618.   long char_count = 0;
  619.   boolean capitalized = false; /* if the word starts with a cap */
  620.  
  621.   for(ch = (unsigned char)line[char_count++]; 
  622.       ch != '\0'; ch = (unsigned char)line[char_count++]){
  623. #ifdef BIO
  624.     boolean alnum = (wordDelimiter(ch) == NOT_DELIMITER);
  625. #else
  626.     boolean alnum = isalnum(ch);
  627. #endif
  628.     if(alnum){
  629.       /* put the character in the word if not too long */
  630.       if(position_in_word == 0)
  631.     capitalized = isupper((unsigned long)ch)?true:false;
  632.       if(position_in_word < MAX_WORD_LENGTH){
  633.     word[position_in_word++] = char_downcase((unsigned long)ch);
  634.       }
  635.     }
  636.     else{ /* not an in a word */
  637.  
  638. /* call the stemmer */
  639. stemmer(word);  
  640.  
  641.       if(position_in_word != 0){
  642.     /* then we have collected a word */
  643.     if(position_in_word >= minwordlen){ /* is it reasonable ? */
  644.       word[position_in_word] = '\0';
  645.       add_word_before_pairs(word,
  646.                 file_position_before_line + char_count, 
  647.                 0L, /* line_pos */
  648.                 weight, 
  649.                 document_id, 
  650.                 (time_t)0L,
  651.                 capitalized,
  652.                 db,
  653.                 word_position, word_pairs);
  654.       word_count++;
  655.     }
  656.     position_in_word = 0;
  657.       }
  658.     }
  659.   }
  660.   /* finish last word */
  661.   if(position_in_word >= minwordlen){ /* is it reasonable ? */
  662.     word[position_in_word] = '\0';
  663.  
  664. /* call the stemmer */
  665. stemmer(word);  
  666.  
  667.     add_word(word,
  668.          file_position_before_line + char_count, 
  669.          0L,        /* line_pos */
  670.          weight, 
  671.          document_id, 
  672.          (time_t)0L,
  673.          0L,
  674. #ifdef WIN32
  675.          db,
  676.          word_position);
  677. #else
  678.          db);
  679. #endif
  680.     word_count++;
  681.   }
  682.  
  683.   /* for debugging
  684.   if(char_count - 1 != strlen(line)) {
  685.     waislog(WLOG_HIGH, WLOG_ERROR, 
  686.         "char_count: %ld, strlen: %ld", char_count, strlen(line));
  687.   }
  688.   */
  689.   if('\n' != line[char_count-2])
  690.     *newline_terminated = false;
  691.   else
  692.     *newline_terminated = true;
  693.  
  694.   *line_length = char_count - 1;
  695.   return(word_count);
  696. }
  697.  
  698. #ifdef WIN32
  699. static int nodecompare _AP((const void* i,const void* j));
  700. #else
  701. static int nodecompare _AP((unsigned long* i,unsigned long* j));
  702. #endif
  703.  
  704. static int
  705. #ifdef WIN32
  706. nodecompare(const void *I,const void *J)
  707. {
  708.   unsigned long *i, *j;
  709.   i = (unsigned long *) I;
  710.   j = (unsigned long *) J;
  711. #else
  712. nodecompare(i,j)
  713. unsigned long *i, *j;
  714. {
  715. #endif
  716.   if (i[0] < j[0])
  717.     return(-1);
  718.   else if (i[0] > j[0])
  719.     return(1);
  720.   else
  721.     return(0);
  722. }
  723.  
  724. #define nodeRange 256 /* 2048 sprint nodes on a full sized machine - should
  725.                  be passed in */
  726. #define iterations_to_reorder 50 /* 1 is best but slow */
  727.  
  728. static void finish_document
  729. #ifndef SOUND 
  730.   _AP((boolean recountHeader, char* header,char* line,long document_id,
  731.        document_table_entry* the_document_table_entry,
  732.        long file_position_before_line,
  733.        long file_position_before_document,database* db,
  734.        boolean word_position, boolean word_pairs,
  735.        int minwordlen));
  736. #else
  737.   _AP((boolean recountHeader, char* header,char* line,long document_id,
  738.        document_table_entry* the_document_table_entry,
  739.        long file_position_before_line,
  740.        long file_position_before_document,database* db,
  741.        boolean word_position, boolean word_pairs,
  742.        int minwordlen, 
  743.        char* type));
  744. #endif
  745.  
  746. static void
  747. finish_document(recountHeader, header,line,document_id,the_document_table_entry,
  748.         file_position_before_line, file_position_before_document,
  749.         db, word_position, word_pairs,
  750. #ifndef SOUND
  751.                 minwordlen)
  752. #else
  753.                 minwordlen, type)
  754. #endif
  755. boolean recountHeader;
  756. char* header;
  757. char* line;
  758. long document_id;
  759. document_table_entry* the_document_table_entry;
  760. long file_position_before_line;
  761. long file_position_before_document;
  762. database* db;
  763. boolean word_position, word_pairs;
  764. int minwordlen;
  765. #ifdef SOUND
  766. char* type;
  767. #endif
  768. { long line_length;
  769.   boolean newline_terminated;
  770.   long number_of_words;
  771.  
  772.  
  773.  if(0 != strlen(header) && recountHeader){
  774.     /* add weights for the header (if there was one) */
  775.     long number_of_words =
  776.       map_over_words(header, document_id, 
  777.              extra_weight_for_header, 
  778.              file_position_before_line-
  779.              file_position_before_document,
  780.              &line_length, 
  781.              &newline_terminated,
  782.              db,
  783.              add_word_before_pairs,
  784.              word_position, word_pairs,
  785. #ifdef SOUND
  786.                      minwordlen, type);
  787. #else
  788.                      minwordlen);
  789. #endif
  790.  
  791.     if(number_of_words == -1)
  792.       waislog(WLOG_HIGH, WLOG_ERROR, "map_over_words failed");
  793.     db->total_word_count += number_of_words;
  794.     the_document_table_entry->document_length += number_of_words;
  795.   }
  796.  
  797.    if(keyword_filename != NULL){
  798.      /* add keywords from keyword file (if specified on command line) */
  799.  
  800.   char *tmpFileName = NULL;
  801.   FILE* keyword_stream = NULL;
  802.   char line[MAX_LINE_LENGTH];
  803.  
  804.      if(keyword_filename != NULL &&
  805.         strlen(keyword_filename) > 1 &&
  806.         !strcmp(keyword_filename+(strlen(keyword_filename)-2), ".Z"))
  807.        /* it's a .Z file.  First, remove the suffix or many things get confused. */
  808.        keyword_filename[(strlen(keyword_filename)-2)] = 0;
  809.  
  810.      if(probe_file(keyword_filename)) {
  811.        keyword_stream = s_fopen(keyword_filename, "r");
  812.      }
  813.      else if(probe_file_possibly_compressed(keyword_filename)) {
  814.          tmpFileName = s_fzcat(keyword_filename);
  815.          if (tmpFileName) {
  816.         keyword_stream = s_fopen(keyword_filename, "r");
  817.         unlink(tmpFileName);
  818.         free(tmpFileName);
  819.          }
  820.      }
  821.  
  822.      if(NULL == keyword_stream)
  823.        waislog(WLOG_HIGH, WLOG_ERROR, 
  824.           "Unable to open keyword file %s", keyword_filename);
  825.      else  { /* read keyword_file, index its contents */
  826.        waislog(WLOG_HIGH, WLOG_INDEX, 
  827.           "Indexing keyword file %s", keyword_filename);
  828.        while(TRUE){
  829.     /* read a line */
  830.     if( !fgets(line, MAX_LINE_LENGTH, keyword_stream) )
  831.       break;  /* eof */
  832.     number_of_words =
  833.       map_over_words(line, document_id, 
  834.              keyword_weight, 
  835.              0,
  836.              &line_length, 
  837.              &newline_terminated,
  838.              db,
  839.              add_word_before_pairs,
  840. #ifdef SOUND
  841.                          word_position, word_pairs, minwordlen, type);
  842. #else
  843.                          word_position, word_pairs,minwordlen);
  844. #endif /* SOUND */
  845.     if(number_of_words == -1)
  846.       waislog(WLOG_HIGH, WLOG_ERROR, "map_over_words failed");
  847.     db->total_word_count += number_of_words;
  848.     the_document_table_entry->document_length += number_of_words;
  849.        }
  850.      s_fclose(keyword_stream);
  851.      }
  852.    }
  853.  
  854.  
  855.   /* store out the document header here */
  856.   the_document_table_entry->headline_id = 
  857.     write_headline_table_entry(header, db);
  858.   if(NULL == line)
  859.     { /* EOF */
  860.       /* if it goes to the end of the file, then
  861.        * set the end_character to 0 so that it is clear that
  862.        * it goes to the end of the file.
  863.        */
  864.       the_document_table_entry->end_character = 0;
  865.     }
  866.   else              /* set the end_character */
  867.     the_document_table_entry->end_character = file_position_before_line;
  868.  
  869.  
  870.   /* 
  871.     waislog("start char: %ld, end char: %ld", 
  872.     the_document_table_entry->start_character,
  873.     the_document_table_entry->end_character);
  874.     */
  875.  
  876.   if (indexingForBeta)
  877.     { /* we need to decide which sprint node this doc will go in.
  878.      for now we will store the sn in the date field, but that
  879.      is temporary
  880.      NOTE that we must subract 1 from document_id, since we want
  881.      a 0 based number
  882.        */
  883.       static unsigned long* nodes = NULL; /* size/node# inited to 0 to 2047 */
  884.       static long minPos;
  885.       unsigned long size;
  886.      
  887.       if (nodes == NULL)
  888.        { long i;
  889.      long startPos;
  890.      time_t temp_time;
  891.  
  892.      nodes = (unsigned long*)s_malloc(sizeof(unsigned long)*nodeRange*2);
  893.      srand((int)time(&temp_time)); /* try to distribute the entries */
  894.      startPos = rand() % nodeRange; /* for indexes with < nodeRng docs */
  895.      for (i = 0; i < nodeRange; i++)
  896.       { nodes[(i * 2) + 1] = (i + startPos) % nodeRange; 
  897.         nodes[i * 2] = 0;
  898.       }
  899.      minPos = 0;
  900. /*printf("init: ");
  901. for (i = 0; i < nodeRange; i++)
  902.  printf("<%lu,%lu> ",nodes[i*2],nodes[(i*2)+1]);
  903. NL();*/
  904.        }
  905.  
  906.       /* place the document in the emptiest node (at minPos) */
  907.       the_document_table_entry->date = (time_t)nodes[(minPos * 2) + 1];
  908.  
  909.       /* increment the size to account for document */
  910.       size = nodes[minPos * 2];
  911.       size += (the_document_table_entry->end_character - 
  912.            the_document_table_entry->start_character);
  913.       nodes[minPos * 2] = size;
  914.  
  915. if ((the_document_table_entry->end_character - 
  916.      the_document_table_entry->start_character) > 100000)
  917.   printf("big doc %lu %s\n",the_document_table_entry->end_character - the_document_table_entry->start_character,header);
  918.  
  919.       minPos++;
  920.  
  921.       /* possibly reorder it */
  922.       if (minPos > iterations_to_reorder)
  923.        { 
  924. long i;
  925.      minPos = 0;
  926. /*printf("before: ");
  927. for (i = 0; i < nodeRange; i++)
  928.  printf("<%lu,%lu> ",nodes[i*2],nodes[(i*2)+1]);
  929. NL();*/
  930. #ifdef WIN32
  931.      qsort((void *)nodes,(size_t)nodeRange,(size_t)(sizeof(unsigned long) * 2),nodecompare);
  932. #else
  933.      qsort((char*)nodes,nodeRange,sizeof(unsigned long) * 2,nodecompare);
  934. #endif
  935. /*printf("after: ");
  936. for (i = 0; i < nodeRange; i++)
  937.  printf("<%lu,%lu> ",nodes[i*2],nodes[(i*2)+1]);
  938. NL();*/
  939. printf("just sorted nodes, min: ");
  940. for (i = 0; i < 10; i++)
  941.  printf("%lu ",nodes[i * 2]);
  942. printf(", max: %lu/%lu\n",nodes[(nodeRange * 2)-2],nodes[(nodeRange * 2)-1]); 
  943.        }
  944.  
  945.  
  946.  
  947. #ifdef old
  948.       sn = (document_id - 1) % 2048; /* 2048 = sn's in a full machine */
  949.  
  950.       /* should also take into account the "fullness" of any particular
  951.      node */
  952.       the_document_table_entry->date = (time_t)sn;
  953. /*      waislog(WLOG_LOW, WLOG_INFO, 
  954.           "put %s in sprint node %ld",header,sn);*/
  955. #endif /* def old */
  956.     }
  957.  
  958.   write_document_table_entry(the_document_table_entry, db);
  959.   cprintf(PRINT_AS_INDEXING, ".");
  960.   total_indexed_file_length =   /* set this so the speed looks right */
  961.     total_indexed_file_length + file_position_before_line;
  962.   total_indexed_file_length =   /* set it back */
  963.     total_indexed_file_length - file_position_before_line;
  964. }
  965.  
  966. #define LENGTH_OF_NEWLINE 1 /* this will be 2 on a PC, I think */  
  967.  
  968. /* void index_text_file(filename,
  969.              separator_function,
  970.              header_function,
  971.              date_function,
  972.              finish_header_function, 
  973.              type,
  974.              db,
  975.              check_for_text_file,
  976.              check_for_file_already_indexed,
  977.              word_position, word_pairs, minwordlen) */
  978.  
  979. void index_text_file(filename, dataops, db,
  980.                      check_for_text_file,
  981.                      check_for_file_already_indexed,
  982.                      word_position, word_pairs,
  983.                      filter_process_in, filter_process_out)      
  984. char* filename;
  985. dataopsrec* dataops;
  986. /*
  987. boolfunc *separator_function;
  988. voidfunc *header_function;
  989. longfunc *date_function;
  990. voidfunc *finish_header_function;
  991. char *type;
  992. */
  993. database* db;
  994. boolean check_for_text_file;
  995. boolean check_for_file_already_indexed;
  996. boolean word_position, word_pairs;
  997. FILE *filter_process_in, *filter_process_out;
  998. {
  999.   /* Adds words to the index for a given file.  
  1000.    * "words" are extracted as strings of alphanumeric chars, whose
  1001.    *  length is >=3 but <toobig.  Long lines are handled in chunks
  1002.    *  so that binary files with embedded text are correctly processed.
  1003.    * The function arguments can be NULL which means it would 
  1004.    *  always answer NULL.  
  1005.    * separator_function is called on every line to see if it 
  1006.    *  separates documents.  
  1007.    * header_function is called on every line so that a headline 
  1008.    *  can be accumulated.  This assumes that it will side effect global 
  1009.    *  variables.
  1010.    * finish_header_function is called when the document is finished 
  1011.    *  (by separator function responding TRUE or EOF) this will return 
  1012.    *  the headline string or NULL. 
  1013.    *  Presumably finish_header_function will use the
  1014.    *  effects of header_function.  finish_header_function 
  1015.    *  will only be called once, so it should clear whatever state 
  1016.    *  header_function has set.
  1017.    * if check_for_text_file then it looks to see if first character
  1018.    *  in the file is a printable character.
  1019.    * if check_for_file_already_indexed then it looks through the filename 
  1020.    *  file to see if the file has not been indexed.  If it has,
  1021.    *  then it is checked to see if it is up-to-date. (it does not 
  1022.    *  kill the old entry (maybe it should)).
  1023.    */
  1024.  
  1025.   long filename_id;
  1026.   document_table_entry the_document_table_entry;
  1027.   long document_id = next_document_id(db);
  1028.  
  1029. /*  FILE* input_stream = s_fopen(filename, "r"); */
  1030.   FILE* input_stream;
  1031.   char *tmpFileName = NULL;
  1032.  
  1033.   long file_position_before_line = 0;
  1034.   long file_position_before_document = 0;
  1035.   long date;
  1036.   int  charsleftover;
  1037.   char leftovers[MAX_LINE_LENGTH];
  1038.   char *p;
  1039.  
  1040. #ifdef WIN32
  1041. /* check the filename is in excluded file name list */
  1042.   if (filename != NULL) {
  1043.     /* Get the filename component from the path */
  1044.     tmpFileName = strrchr(filename, '\\');
  1045.     if (tmpFileName != NULL)
  1046.       tmpFileName++;
  1047.     else
  1048.       tmpFileName = filename;
  1049.     p = ExcludeFiles;
  1050.     while (*p) {
  1051.         if (WildcardMatch(p,tmpFileName)) {
  1052.             /* The filename is matched in the exclude list */
  1053.             waislog(WLOG_MEDIUM, WLOG_INDEX,
  1054.             "File: %s excluded from index",  filename);
  1055.             return;
  1056.         }
  1057.         p += strlen(p); /* Point to end of string */
  1058.         p++;            /* Point to next filename */
  1059.     }
  1060.   }
  1061. #endif  
  1062.  
  1063.    if(filename != NULL &&
  1064.       strlen(filename) > 1 &&
  1065.       !strcmp(filename+(strlen(filename)-2), ".Z"))
  1066.      /* it's a .Z file.  First, remove the suffix or many things get confused. */
  1067.      filename[(strlen(filename)-2)] = 0;
  1068.      
  1069. /* multitype extensions */
  1070. /* 
  1071.    If dataops->multitype (primary and secondary type) is defined, then
  1072.    we need to index filenames that have an extension with the
  1073.    dataops->type (primary type) and skip all other files.
  1074.    
  1075.    The only problem with this approach is that we may loose files which 
  1076.    dont have an instance of the primary file type 
  1077. */
  1078.    if ( (dataops->multitype != NULL) && 
  1079.     strcmp(filename+(strlen(filename)-strlen(dataops->type)), dataops->type)) {
  1080.     waislog(WLOG_HIGH, WLOG_INFO, "Skipping file: %s",
  1081.         filename);
  1082.     return;
  1083.    }
  1084.  
  1085.  
  1086.  
  1087.    if(probe_file(filename)) {
  1088.      input_stream = s_fopen(filename, "r");
  1089.    }
  1090.    else if(probe_file_possibly_compressed(filename)) {
  1091.        tmpFileName = s_fzcat(filename);
  1092.        if (tmpFileName) {
  1093.       input_stream = s_fopen(tmpFileName, "r");
  1094.       unlink(tmpFileName);
  1095.       free(tmpFileName);
  1096.        }
  1097.    }
  1098.  
  1099. /* end of this long one */
  1100.  
  1101.  
  1102.  
  1103.   if(NULL == input_stream){
  1104.     waislog(WLOG_HIGH, WLOG_ERROR, 
  1105.         "File %s does not exist", filename);
  1106.     /* then the is not a valid file to be indexed */
  1107.     return;
  1108.   }
  1109.   if(check_for_file_already_indexed){
  1110.     time_t time;
  1111.     char full_path[MAX_FILENAME_LEN];
  1112.     truename(filename, full_path);
  1113.     if(true == filename_in_database(full_path, dataops->type, &time, db)){
  1114.       /* check that it is the same time as this file */
  1115.       if(time == file_write_date(filename)){
  1116.     waislog(WLOG_HIGH, WLOG_INDEX, 
  1117.         "File %s already indexed", filename);
  1118.     s_fclose(input_stream);
  1119.     return;
  1120.       }
  1121.     }
  1122.   }
  1123.     
  1124.   /* Make the current filename accessible via global variables.
  1125.    * Increment current_filecount so routines can efficiently detect
  1126.    * changes in the current file.
  1127.    * -- Prentiss Riddle, Rice ONCS, riddle@rice.edu, 5/6/92
  1128.    */
  1129.  
  1130.   if(current_filename == NULL) current_filename = s_malloc(MAX_FILENAME_LEN+1);
  1131.  
  1132.   if (URL_prefix && !strncmp(filename, URL_trim, MIN(strlen(URL_trim), strlen(filename)))) {
  1133.     /* trim capable */
  1134.     strcpy(current_filename, URL_prefix);
  1135.     strcat(current_filename, filename+strlen(URL_trim));
  1136.   } else
  1137.   strncpy(current_filename, filename, MAX_FILENAME_LEN);
  1138.   current_filecount++;
  1139.  
  1140.   if(check_for_text_file){ 
  1141.     /* if we need this to be a text file, check the first character
  1142.        for a printable character */
  1143.     long ch = fgetc(input_stream);
  1144.     /* printf("First character is '%c'\n", ch); */
  1145.     if(EOF == ch || (!isprint(ch) && !isspace(ch))){
  1146.       s_fclose(input_stream);
  1147.       return;
  1148.     }
  1149.     ungetc(ch, input_stream);
  1150.   }
  1151.   
  1152.  
  1153. /* multitype extensions */
  1154.  
  1155.   /* write out the filename */
  1156.   if ( dataops->multitype != NULL ) {
  1157.      filename_id = write_filename_table_entry(filename, dataops->multitype, db);
  1158.   }
  1159.   else {
  1160.      filename_id = write_filename_table_entry(filename, dataops->type, db);
  1161.   }
  1162.   
  1163.   
  1164.   /*  (if (not *drop_table*) (make_drop_table)) maybe put in later */
  1165.   
  1166.   header_flag_1 = NULL;
  1167.   the_document_table_entry.filename_id = filename_id;
  1168.   the_document_table_entry.start_character = 0;
  1169.   the_document_table_entry.document_length = 0;
  1170.   the_document_table_entry.number_of_lines = 0;
  1171.   the_document_table_entry.date = 0;
  1172.   charsleftover = 0;
  1173.  
  1174.   while(TRUE){
  1175.     long line_length;
  1176.     boolean newline_terminated;
  1177.     char line[MAX_LINE_LENGTH];
  1178.     char header[MAX_LINE_LENGTH];
  1179.     char* read_line_result;
  1180.     boolean eof;
  1181.  
  1182.     int i, cut, charsread;
  1183.  
  1184.     /*  
  1185.      *  Read a block of up to MAX_LINE_LENGTH chars from a single "line"
  1186.      *  and extract a whole number of words from it.  Save partial words
  1187.      *  lying at edge of block in the leftovers[] array for the next loop.
  1188.      *  This accomodates binary files formats with embedded text, which
  1189.      *  often have very long "lines".   [burchard@geom.umn.edu 4/16/93]
  1190.      */
  1191.  
  1192.     /* prefill line with leftovers before reading in new data */
  1193.     beFriendly();
  1194.  
  1195.     for(i=0; i<charsleftover; i++) line[i] = leftovers[i];
  1196.     line[charsleftover] = '\0';
  1197.     read_line_result = fgets(line + charsleftover,
  1198.       MAX_LINE_LENGTH - charsleftover, input_stream);
  1199.  
  1200.     /* increment line count if we have finished reading a "line" */
  1201.     charsread = strlen(line);
  1202.     if(charsread>charsleftover && line[charsread-1]=='\n')
  1203.  
  1204.       the_document_table_entry.number_of_lines++;
  1205.  
  1206.     /* save word frag at end as leftovers (unless it fills whole block) */
  1207.     for(cut=charsread-1; cut>=0; cut--)
  1208.       if(!isascii(line[cut]) || !isalnum(line[cut])) break;
  1209.     if(++cut <= 0) charsleftover = 0;
  1210.     else {
  1211.       for(charsleftover=0, i=cut; i<charsread; charsleftover++, i++) 
  1212.  
  1213.         leftovers[charsleftover] = line[i];
  1214.       charsread -= charsleftover;
  1215.     }
  1216.     line[charsread] = '\0';
  1217.  
  1218.     /* don't say EOF yet if we still have leftovers to process */
  1219.     eof = (!read_line_result && !charsread && !charsleftover);
  1220.  
  1221.  
  1222.     header[0] = '\0';       /* set it to the empty string */
  1223.  
  1224.     if(eof ||
  1225.        ((NULL != dataops->separator_function) && dataops->separator_function(line)) || (keyword_filename != NULL) ){
  1226.  
  1227.     /* tell this function that there is not more to process */
  1228.     if (keyword_filename != NULL) {
  1229.         eof = true; 
  1230.     } 
  1231.   
  1232.  
  1233.       /* we are processing a separator, therefore we should
  1234.        * finish off the last document, and start a new one
  1235.        */
  1236.       if(NULL != dataops->finish_header_function){
  1237.     dataops->finish_header_function(header);
  1238.  
  1239. /* call Victor Nettoyage :-( */
  1240.         (void)cleanHeadline(header);
  1241.         
  1242.       }
  1243.       if(0 == strlen(header)){
  1244.     char full_path[1000];
  1245.     char directory[1000];
  1246.     if (!URL_prefix) {
  1247.       truename(filename, full_path);
  1248.       sprintf(header, "%s   %s", pathname_name(full_path),
  1249.         pathname_directory(full_path, directory));
  1250.     } else
  1251.           strncpy(header, current_filename, MAX_FILENAME_LEN);
  1252.       }
  1253.       if(the_document_table_entry.number_of_lines > 0)
  1254.         the_document_table_entry.number_of_lines--; /* dont count separator */
  1255.       /* finish off the last */
  1256.       finish_document( dataops->extraheaderweight,
  1257.               header, line, document_id,
  1258.               &the_document_table_entry,
  1259.               eof?  /* if EOF, use file length */
  1260.               file_length(input_stream):file_position_before_line, 
  1261.               file_position_before_document,
  1262.               db, word_position, word_pairs,
  1263. #ifndef SOUND
  1264.                       dataops->minwordlen);
  1265. #else
  1266.                       dataops->minwordlen, dataops->indextype);
  1267. #endif
  1268.       /* initialize the next one */
  1269.       the_document_table_entry.filename_id = filename_id;
  1270.       the_document_table_entry.start_character = file_position_before_line;
  1271.       the_document_table_entry.number_of_lines = 1; /* count separator */
  1272.       the_document_table_entry.date = 0;
  1273.       the_document_table_entry.document_length = 0;
  1274.       file_position_before_document = file_position_before_line;
  1275.  
  1276.       document_id = next_document_id(db);   
  1277.  
  1278.       if(!eof)
  1279.     {           /* not EOF */
  1280.          if(NULL != dataops->header_function){
  1281.             dataops->header_function(line);
  1282.           }
  1283.      if (dataops->date_function != NULL &&
  1284.               (date = dataops->date_function(line)) > 0)
  1285.             the_document_table_entry.date = date;
  1286.      /* dgg -- don't know where this goes. */
  1287.  
  1288.          if (dataops->addseparatorwords) { /* dgg */
  1289.               long number_of_words;
  1290.           number_of_words = map_over_words(line, document_id, dataops->repeat_weight,
  1291.                                          file_position_before_line -
  1292.                                          file_position_before_document,
  1293.                                          &line_length,
  1294.                                          &newline_terminated,
  1295.                                          db,
  1296.                                          add_word_before_pairs,
  1297.                                          word_position, word_pairs,
  1298. #ifdef SOUND
  1299.                                          dataops->minwordlen,
  1300.                                          dataops->indextype);
  1301. #else
  1302.                                          dataops->minwordlen);
  1303. #endif /* SOUND */
  1304.           the_document_table_entry.document_length += number_of_words;
  1305.           len_of_files_since_last_delete += number_of_words;
  1306.           len_of_files_since_last_flush += number_of_words;
  1307.     }
  1308.     else {
  1309.       line_length = strlen(line);
  1310.       newline_terminated = true;
  1311.       }
  1312.     }
  1313.       else{         /* EOF */
  1314.     /* printf("closing the file\n"); */
  1315.     s_fclose(input_stream);
  1316.     return;
  1317.       }
  1318.     }
  1319.         
  1320.     else{              
  1321.       /* not a separator or EOF so process the line */
  1322.       long number_of_words;
  1323.       if (dataops->date_function != NULL && 
  1324.       the_document_table_entry.date == 0 &&
  1325.            (date = dataops->date_function(line)) > 0) 
  1326.     the_document_table_entry.date = date;
  1327.  
  1328.       if(NULL != dataops->header_function) dataops->header_function(line);
  1329.  
  1330.       if(index_contents ) {
  1331.         if( _indexable_section) {
  1332.       number_of_words = map_over_words(line, document_id, dataops->repeat_weight, 
  1333.                      file_position_before_line -
  1334.                      file_position_before_document,
  1335.                      &line_length, 
  1336.                      &newline_terminated,
  1337.                      db,    
  1338.                      add_word_before_pairs,
  1339.                      word_position, word_pairs,
  1340. #ifdef SOUND
  1341.                                          dataops->minwordlen,
  1342.                                          dataops->indextype);
  1343. #else
  1344.                                          dataops->minwordlen);
  1345. #endif
  1346.           if(number_of_words == -1)
  1347.             waislog(WLOG_HIGH, WLOG_ERROR, "map_over_words failed");
  1348.           the_document_table_entry.document_length += number_of_words;
  1349.           len_of_files_since_last_delete += number_of_words;
  1350.           len_of_files_since_last_flush += number_of_words;
  1351.           db->total_word_count += number_of_words;
  1352.         }
  1353.         else
  1354.           newline_terminated = 0;
  1355.       }
  1356.     }
  1357.     if(newline_terminated)
  1358.       file_position_before_line += (line_length + 
  1359.                     LENGTH_OF_NEWLINE /* in case of crlf */
  1360.                     - 1 /* fgets gets one newline */
  1361.                     );
  1362.     else
  1363.       file_position_before_line = ftell(input_stream);
  1364.  
  1365.     
  1366.     /* for debugging
  1367.     if(file_position_before_line != ftell(input_stream)) {
  1368.       waislog(WLOG_LOW, WLOG_INFO, "ftell: %ld, computed ftell: %ld", 
  1369.          ftell(input_stream),
  1370.          file_position_before_line);
  1371.          }
  1372.          */ 
  1373.  
  1374.   }
  1375. }
  1376.  
  1377.  
  1378.  
  1379.  
  1380. /* return TRUE if it is a directory, FALSE otherwise */
  1381. boolean directoryp(file)
  1382. char *file;
  1383.  
  1384. {
  1385. #ifdef THINK_C
  1386.   return(false);
  1387. #else
  1388.   struct stat stbuf;
  1389.   if(stat(file, &stbuf) == -1)
  1390.     return(FALSE);
  1391.   if((stbuf.st_mode & S_IFMT) == S_IFDIR)
  1392.     return(true);
  1393.   return(FALSE);
  1394. #endif
  1395. }
  1396.  
  1397. /* return true if it is a file, FALSE otherwise */
  1398. boolean filep(file)
  1399. char *file;
  1400. {
  1401. #ifdef THINK_C
  1402.  return(probe_file(file));
  1403. #else
  1404.   struct stat stbuf;
  1405.   if(stat(file, &stbuf) == -1)
  1406.     return(FALSE);
  1407.   if(!((stbuf.st_mode & S_IFMT) == S_IFDIR))
  1408.     return(true);
  1409.   return(FALSE);
  1410. #endif
  1411. }
  1412.  
  1413.  
  1414. /* recursively indexes the directory specified. 
  1415.  * If it is a file, then index it. 
  1416.  */
  1417. void index_directory(file, dataops,  db,
  1418.              check_for_text_file,
  1419.              check_for_file_already_indexed,
  1420.                      word_position, word_pairs,
  1421. #ifndef WIN32
  1422.                      filter_process_in, filter_process_out)
  1423. #else
  1424.              filemask, filter_process_in, filter_process_out)
  1425. #endif
  1426. char *file;
  1427. dataopsrec*  dataops;
  1428. database* db;
  1429. boolean check_for_text_file;
  1430. boolean check_for_file_already_indexed;
  1431. boolean word_position, word_pairs;
  1432. #ifdef WIN32
  1433. char *filemask;
  1434. #endif
  1435. FILE *filter_process_in, *filter_process_out;
  1436. {
  1437. #ifndef THINK_C
  1438. #ifndef WIN32
  1439.   long i, j;
  1440. #endif
  1441.  
  1442.   if(filep(file)){
  1443.     waislog(WLOG_MEDIUM, WLOG_INDEX,
  1444.         "Indexing file: %s",  file);
  1445.     index_text_file(file, dataops, db,
  1446.             check_for_text_file,
  1447.             check_for_file_already_indexed,
  1448.                     word_position, word_pairs,NULL,NULL);
  1449.   }
  1450.   else if(directoryp(file)){
  1451.     /* for each name in the directory, call ourselves back */
  1452. #ifdef WIN32
  1453.     HANDLE hSearch;
  1454.     WIN32_FIND_DATA FindData;
  1455.     char Name[MAX_PATH+1];
  1456.     
  1457.     /* Index the files which match the mask */
  1458.     strncpy(Name,file,MAX_PATH);
  1459.     Name[MAX_PATH] = '\0';
  1460.     strncat(Name,"\\",MAX_PATH);
  1461.     Name[MAX_PATH] = '\0';
  1462.     strncat(Name,filemask,MAX_PATH);
  1463.     Name[MAX_PATH] = '\0';
  1464.     hSearch = FindFirstFile(Name,&FindData);
  1465.     if (hSearch!=INVALID_HANDLE_VALUE) {
  1466.         while (TRUE) {
  1467.             if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)==0) {
  1468.                 /* Index the file */
  1469.                 strncpy(Name,file,MAX_PATH);
  1470.                 Name[MAX_PATH] = '\0';
  1471.                 strncat(Name,"\\",MAX_PATH);
  1472.                 Name[MAX_PATH] = '\0';
  1473.                 strncat(Name,FindData.cFileName,MAX_PATH);
  1474.                 Name[MAX_PATH] = '\0';
  1475.                 waislog(WLOG_MEDIUM, WLOG_INDEX,
  1476.                     "Indexing file: %s",  Name);
  1477.                 index_text_file(Name, dataops, db,
  1478.                         check_for_text_file,
  1479.                         check_for_file_already_indexed,
  1480.                         word_position, word_pairs,
  1481.                         filter_process_in, filter_process_out);
  1482.             }
  1483.             if (!FindNextFile(hSearch,&FindData)) break;
  1484.         }
  1485.         FindClose(hSearch);
  1486.     }
  1487.     /* Now do all the subdirectories */
  1488.     strncpy(Name,file,MAX_PATH);
  1489.     Name[MAX_PATH] = '\0';
  1490.     strncat(Name,"\\*",MAX_PATH);
  1491.     Name[MAX_PATH] = '\0';
  1492.     hSearch = FindFirstFile(Name,&FindData);
  1493.     if (hSearch!=INVALID_HANDLE_VALUE) {
  1494.         while (TRUE) {
  1495.             if (strcmp(FindData.cFileName,".")!=0
  1496.              && strcmp(FindData.cFileName,"..")!=0
  1497.              && (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  1498.                 /* Index the directory */
  1499.                 strncpy(Name,file,MAX_PATH);
  1500.                 Name[MAX_PATH] = '\0';
  1501.                 strncat(Name,"\\",MAX_PATH);
  1502.                 Name[MAX_PATH] = '\0';
  1503.                 strncat(Name,FindData.cFileName,MAX_PATH);
  1504.                 Name[MAX_PATH] = '\0';
  1505.                 index_directory(Name, dataops, db,
  1506.                      check_for_text_file,
  1507.                      check_for_file_already_indexed,
  1508.                      word_position, word_pairs, filemask,NULL,NULL);
  1509.             }
  1510.             if (!FindNextFile(hSearch,&FindData)) break;
  1511.         }
  1512.         FindClose(hSearch);
  1513.     }
  1514. #else
  1515.     struct dirent **list;
  1516.  
  1517.     if ((i = scandir(file, &list, NULL, NULL)) < 0) {
  1518.       return;
  1519.     }
  1520.     for(j = 0; j < i; j++) {
  1521.       char name[1000];      /* max filename size */
  1522.  
  1523.       if(strcmp(list[j]->d_name, ".") == 0
  1524.      || strcmp(list[j]->d_name, "..") == 0
  1525.      )
  1526.     continue;
  1527.  
  1528.       strcpy(name, file);   /* copy the filename into the name variable */
  1529.       strcat(name, "/");
  1530.       strcat(name, list[j]->d_name);
  1531.       index_directory(name, dataops, db,
  1532.               check_for_text_file,
  1533.               check_for_file_already_indexed, 
  1534.                       word_position, word_pairs,NULL,NULL);
  1535.     }
  1536.     if(list != NULL) {
  1537.       for (j = 0; j < i; j++)
  1538.     if(list[j] != NULL) free((char *)list[j]);
  1539.       free((char *)list);
  1540.     }
  1541. #endif /* def WIN32 */
  1542.   }
  1543. #endif /* ndef THINK_C */
  1544. }
  1545.  
  1546.  
  1547. /* returns a pointer to a string with good stuff */
  1548. char *cleanHeadline (headline)
  1549. char *headline;
  1550. {
  1551.   long length = strlen(headline) + 1; /* include the trailing null */
  1552.   long i,j;
  1553.   Boolean spaceFlag = false;
  1554.  
  1555.  
  1556.   /* delete leading spaces */
  1557. #ifdef WIN32
  1558.   for(i = 0L; i < (long)strlen(headline); i++){
  1559. #else
  1560.   for(i = 0L; i < strlen(headline); i++){
  1561. #endif
  1562. /*    if(isprint(headline[i])){ */
  1563.     if(isgraph(headline[i])){
  1564.       break;
  1565.     }
  1566.   }
  1567.  
  1568.   /* and move it */
  1569.   memcpy(headline, headline+i, length);
  1570.  
  1571.  
  1572.   /* 
  1573.   ** - replace all the \n and \r with a space, avoid putting 
  1574.   **   two spaces one after the other
  1575.   */
  1576.   headline = headline + i;
  1577.   /* replace carriage returns and line feeds */
  1578. #ifdef WIN32
  1579.   for (i = 0L, j = 0L; i < (long)strlen(headline)+1; i++) {
  1580. #else
  1581.   for (i = 0L, j = 0L; i < strlen(headline)+1; i++) {
  1582. #endif
  1583.     if ((headline[i] != '\r') && (headline[i] != '\n')) {
  1584.       headline[j++] = headline[i];
  1585.       spaceFlag = false;
  1586.     }
  1587.     else {
  1588.     
  1589.         if ( spaceFlag == true ) {
  1590.            j++;
  1591.         }
  1592.         else {
  1593.           headline[j++] = ' ';
  1594.         }
  1595.         
  1596.         spaceFlag = true;
  1597.     }
  1598.   }
  1599.   
  1600.  
  1601.   /* delete trailing stuff */
  1602.   for(i = strlen(headline) - 1L ; i > 0; i--){
  1603. /*    if(isprint(headline[i])){ */
  1604.     if(isgraph(headline[i])){
  1605.       break;
  1606.     }
  1607.     headline[i] = '\0';
  1608.   }
  1609.   
  1610.   return(headline);
  1611. }
  1612.  
  1613.  
  1614.